import { Project, Node } from "ts-morph";
import fs from "fs";
import path from "path";
import moment from "moment";
import { fileURLToPath } from 'url';

let libName = "unknown";
const fileName = fileURLToPath(import.meta.url);





// 需要忽略的文件夹
const ignoredFolders = [
    "node_modules",
    "example",
    "examples",
    "test",
    "tests",
    "__tests__",
    "docs",
    "documentation",
    "dist",
    "build",
    "perf",
    "scripts",
    "benchmark",
    "__benchmark__",
    "coverage",
    "config",
    "mocks",
    "fixtures",
];

/**
 * 将函数信息添加到 functionsData 中
 * @param {string} name - 函数名称
 * @param {string} body - 函数体
 * @param {string} type - 函数类型
 * @param {object} libInfo - 包含库信息的对象 { name, version, baseDir }
 * @param {string} filePath - 当前文件的绝对路径，用于计算相对路径
 */
function addFunctionToData(name, body, type, libInfo, filePath, startLineNumber, endLineNumber) {
    body = ensureBraces(body);
    if (body === "{}") {
        return;
    }
    // 相对路径：基于当前库的根目录
    const relativePath = path.relative(libInfo.baseDir, filePath);
    functionsData.totalFunctions += 1;
    functionsData.functions.push({
        type: type,
        name: name,
        body: body,
        library: libInfo.name,
        version: libInfo.version,
        relativePath: relativePath.replace(/\\/g, "/"),
        startLineNumber: startLineNumber,
        endLineNumber: endLineNumber
    });
}

// 保证函数体符合大括号封装格式
function ensureBraces(str) {
    const regex = /^\{[\s\S]*}$/;
    if (!regex.test(str)) {
        str = "{" + str + "}";
    }
    return str;
}

// iife 函数数据
const iifefunctionsData = {
    functions: [],
    total: 0,
};

/**
 * 处理文件，提取函数信息
 * @param {string} filePath - 文件的绝对路径
 * @param {object} libInfo - 库信息对象
 */
function processFile(filePath, libInfo) {
    const sourceFile = project.addSourceFileAtPath(filePath);
    sourceFile.forEachDescendant(node => {
        if (Node.isFunctionDeclaration(node)) {
            //先看这个函数是不是嵌套定义的，如果是就不进行提取了
            const LineNumber  =  node.getEndLineNumber()- node.getStartLineNumber()
            let isNestedFunction = false
            node.forEachDescendant(
                (child,traversal)=>{
                    if(Node.isFunctionDeclaration(child)){
                        console.log("nested funcions")
                        isNestedFunction = true
                        traversal.stop()
                    }
                }
            )
            if(LineNumber > 1000 || isNestedFunction){
                return;
            }

            const funcName = node.getName() || "anonymous";
            const funcBody = node.getBody()?.getText() || "{}";
            addFunctionToData(funcName, funcBody, "function", libInfo, filePath, node.getStartLineNumber(), node.getEndLineNumber());
        } else if (Node.isArrowFunction(node)) {
            const LineNumber  =  node.getEndLineNumber()- node.getStartLineNumber()
            if(LineNumber > 1000 ){
                return;
            }
            const funcName = "anonymous";
            const funcBody = node.getBody().getText() || "{}";
            addFunctionToData(funcName, funcBody, "arrow function", libInfo, filePath, node.getStartLineNumber(), node.getEndLineNumber());
        } else if (Node.isMethodDeclaration(node)) {
            const funcName = node.getName() || "anonymous";
            const funcBody = node.getBody()?.getText() || "{}";
            addFunctionToData(funcName, funcBody, "method", libInfo, filePath, node.getStartLineNumber(), node.getEndLineNumber());
        } else if (Node.isFunctionExpression(node)) {
            const parentNode = node.getParent();
            if (Node.isParenthesizedExpression(parentNode)) {
                const rootNode = parentNode.getParent();
                if (Node.isCallExpression(rootNode)) {
                    const func = {
                        body: rootNode.getText(),
                        path: filePath,
                    };
                    iifefunctionsData.total += 1;
                    iifefunctionsData.functions.push(func);

                    return;
                }
            } else if (Node.isCallExpression(parentNode)) {
                const func = {
                    body: parentNode.getText(),
                    path: filePath,
                };
                iifefunctionsData.total += 1;
                iifefunctionsData.functions.push(func);

                return;
            }

            const LineNumber  =  node.getEndLineNumber()- node.getStartLineNumber()
            if(LineNumber > 1000 ){
                return;
            }
            const funcName = "anonymous";
            const funcBody = node.getBody()?.getText() || "{}";
            addFunctionToData(funcName, funcBody, "function expression", libInfo, filePath, node.getStartLineNumber(), node.getEndLineNumber());
        }
    });
}

/**
 * 遍历库中所有文件（递归），提取函数信息。
 * @param {string} currentPath - 当前遍历的目录
 * @param {object} libInfo - 当前库信息对象，用于传递库名称、版本及根目录
 */
function traverseLibraryFiles(currentPath, libInfo) {
    const items = fs.readdirSync(currentPath);
    for (const item of items) {
        // 如果遇到 packages 目录直接跳过
        if (item === "packages") continue;

        const itemPath = path.join(currentPath, item);
        const stat = fs.statSync(itemPath);

        if (stat.isDirectory()) {
            const hiddenFolderPattern = /^\..+/;
            const folderName = path.basename(itemPath);
            if (ignoredFolders.includes(folderName) || hiddenFolderPattern.test(folderName)) {
                continue;
            }
            traverseLibraryFiles(itemPath, libInfo);
        } else if (stat.isFile()) {
            const fileName = path.basename(itemPath);
            const isDotXxJs = /\.(?!.*\.).+\.js$/.test(fileName);
            if (isDotXxJs) continue;
            const specialIgnoredPatterns = [
                /\.d\.ts$/,
                /\.min\.js$/,
                /\.prod\.js$/,
                /\.test\.ts$/,
                /\.light\.js$/,
                /\.full\.js$/,
                /\.eslintrc\.js$/,
                /\.config\.js$/,
                /\.config\.ts$/,
                /\.conf\.js$/,
                /\.spec\.(ts|js|mjs|cjs)$/
            ];
            if (specialIgnoredPatterns.some(pattern => pattern.test(fileName))) continue;
            if (["Gruntfile.js"].includes(fileName)) continue;

            const fileExtension = path.extname(fileName);
            const processedExtensions = [".js", ".ts", ".mjs", ".cjs"];
            if (processedExtensions.includes(fileExtension)) {
                processFile(itemPath, libInfo);
            }
        }
    }
}

/**
 * 处理 packages 下的子库。如果当前库存在 packages 目录，
 * 则将其中每个文件夹视为一个独立的子库，并单独提取函数。
 * @param {string} libPath - 当前库的路径
 * @param {object} originalLibInfo - 当前库信息对象
 */
function processSubLibraries(libPath, originalLibInfo) {
    const packagesPath = path.join(libPath, "packages");
    if (fs.existsSync(packagesPath) && fs.statSync(packagesPath).isDirectory()) {
        const subItems = fs.readdirSync(packagesPath);
        for (const subItem of subItems) {
            const subItemPath = path.join(packagesPath, subItem);
            if (fs.statSync(subItemPath).isDirectory()) {
                // 新库名称为 "原库名称-子文件夹名称"，并以子库根目录作为基准
                const newLibInfo = {
                    name: `${originalLibInfo.name}-${subItem}`,
                    version: "unknown",
                    baseDir: subItemPath,
                };
                processSubLibrary(subItemPath, newLibInfo);
            }
        }
    }
}

/**
 * 处理单个子库：读取 package.json 获取版本（若存在），然后遍历子库文件。
 * @param {string} subLibPath - 子库的路径
 * @param {object} libInfo - 子库对应的库信息对象
 */
function processSubLibrary(subLibPath, libInfo) {
    const packageJsonPath = path.join(subLibPath, "package.json");
    if (fs.existsSync(packageJsonPath)) {
        try {
            const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8");
            const packageJson = JSON.parse(packageJsonContent);
            libInfo.version = packageJson.version || "unknown";
            if(packageJson.name)
            libInfo.name = packageJson.name ;
        } catch (e) {
            console.error(`Error reading package.json in ${subLibPath}: ${e}`);
        }
    }
    traverseLibraryFiles(subLibPath, libInfo);
}

/**
 * 处理一个库（单个目录）的入口函数：
 * 1. 读取 package.json（若存在）获取版本信息
 * 2. 构建库信息对象
 * 3. 如果存在 packages 目录则单独处理子库
 * 4. 遍历当前库中除 packages 目录外的所有文件
 * @param {string} libPath - 库的路径
 */
function processLibrary(libPath) {
    const packageJsonPath = path.join(libPath, "package.json");
    const libInfo = {
        name: library,
        version: "unknown",
        baseDir: libPath,
    };
    // 如果这个文件夹没有 package.json 文件，说明还是嵌套库，如果没有packages文件夹，就把每个文件夹都当做是库
    if (fs.existsSync(packageJsonPath)) {
        try {
            const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8");
            const packageJson = JSON.parse(packageJsonContent);
            libInfo.version = packageJson.version || "unknown";
        } catch (e) {
            console.error(`Error reading package.json in ${libPath}: ${e}`);
        }
    }else{

    }

    // 先处理 packages 下的子库（独立提取）
    processSubLibraries(libPath, libInfo);
    // 再处理当前库中除 packages 外的文件
    traverseLibraryFiles(libPath, libInfo);
}
function ensureDirectoryExists(dir) {
    if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
    }
}
// 保存数据到文件
function saveDataToFile(filePath, data) {
    ensureDirectoryExists(path.dirname(filePath));
    fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
    console.log(`Data saved to ${filePath}`);
}
// 主函数
function main() {
    try {
        if (fs.statSync(targetDir).isDirectory()) {
            processLibrary(targetDir);
        }else {
            console.log(`The target directory does not exist: ${targetDir}`);
        }
        cacheDir = js_output_path
        if(cacheDir === "unknown"){
            cacheDir = "./"
        }
        // 生成带时间戳的文件名
        const timestamp = moment().format("YYYYMMDD_HHmm");
        const filename = path.join(
            cacheDir,
            "JsonFiles",
            functionsData.functions[0].library,
            `${path.basename(functionsData.functions.length+"functions", '.json')}_${timestamp}.json`
        );

        const filterName = path.join(
            cacheDir,
            "filterOutput",
            `iife_filter_functions_${timestamp}.json`
        );

        // 保存文件
        saveDataToFile(filename, functionsData);
        saveDataToFile(filterName, iifefunctionsData);

        // 输出结果路径
        console.log(JSON.stringify({
            status: "success",
            mainOutput: filename,
            iifeOutput: filterName
        }));

    } catch (error) {
        console.error(JSON.stringify({
            status: "error",
            message: error.message,
            stack: error.stack
        }));
        process.exit(1);
    }
}

// 初始化全局函数数据
const functionsData = {
    totalFunctions: 0,
    functions: [],
};

const project = new Project();

/**
 * 待提取函数的目标目录（支持库目录或单个文件路径）
 * 注意：如果输入的是一个库目录，则该目录下所有子库都会被处理。
 * @type {string[]}
 */
const args = process.argv.slice(2);
const [targetDir,js_output_path,library] = args;
// const targetDir = "D:\\Project\\testDir\\testLibs\\react-native"
// const js_output_path = "D:\\Project\\testDir\\1"
// 执行主函数
        // 临时文件夹存放临时文件

// 获取父级目录中的某个特定文件夹中的 JSON 文件路径
let cacheDir ="unknown";


main();
